def hi(name = "yasoob"):
    return "hi "+name

print("test 1")
print(hi())

print("test 2")
greet = hi
print(greet())

del hi
#print(hi())

print(greet())

print("define function inside a function")
def hi(name = "yasoob"):
    print("now you are inside the hi() function")

    def greet():
        return "now you are in the greet() function"

    def welcome():
        return "now you are in the welcome() function"

    print(greet())
    print(welcome())
    print("now you are back in the hi() function")

print("test 1")
hi()
print("test 2")
greet()

print("function return a function")
def hi(name = "yasoob"):
    def greet():
        return "now you are in the greet() function"

    def welcome():
        return "now you are in the welcome() function"

    if name == "yasoob":
        return greet
    else:
        return welcome

a = hi()
print(a)
print(a())

print("pass a function to another function")
def hi():
    return "hi yasoob"

def doSomethingBeforeHi(func):
    print("i am doing some boring work before executing hi()")
    print(func())

doSomethingBeforeHi(hi)

print("decorator test ") 

def a_new_decorator(a_func):
    def wrapTheFunction():
        print("I am doing some boring work before executing a a_func()")
        a_func()
        print("I am doing some boring work after executing a_func()")
    return wrapTheFunction

def a_function_requiring_decoration():
    print("I am the function which needs some decoration to remove my foul smell")

a_function_requiring_decoration()
a_function_requiring_decoration = a_new_decorator(a_function_requiring_decoration)
a_function_requiring_decoration()

print("use decorator ")
@a_new_decorator
def a_function_requiring_decoration():
    print("decorator test")

a_function_requiring_decoration()
print(a_function_requiring_decoration.__name__)  #not right

print("wrap function")
from functools import wraps


def a_new_decorator(a_func):
    @wraps(a_func)
    def wrapTheFunction():
        print("I am doing some boring work before executing a_func()")
        a_func()
        print("I am doing some boring work after executing a_func()")
    return wrapTheFunction
@a_new_decorator
def a_function_requiring_decoration():
    print("I am the function which needs some decoration to remove my foul smell")
print(a_function_requiring_decoration.__name__)

print("decoration func test 2")
from functools import wraps


def decorator_name(f):
    # print("can run: ", can_run)
    can_run = True #这里有can_run的定义，则会译为局部变量。上面用到的can_run就是错误的，引用的未定义的局部变量。
    @wraps(f)
    def decorated(*args, **kwargs):
        if not can_run:   #如果函数中没有这个变量的定义，则会看外面有没有这个全局变量。如果没有，就会报错。如果有，就用全局变量的值。这里是要外面有全局变量才行
            return "Function will not run"
        return f(*args, **kwargs)
    return decorated

@decorator_name
def func():
    return("Function is running")

can_run = True #不明白，这里的参数。认为就是相当于全局变量，在装饰器函数中还能起作用
print(func())

can_run = False
print(func())

print("decorate function test 3")
from functools import wraps


def logit(func):
    @wraps(func)
    def with_logging(*args, **kwargs):
        print(func.__name__+"was called")
        return func(*args, **kwargs)
    return with_logging

@logit
def addition_func(x):
    return x+x
result = addition_func(4)
print(result)

print("decoracte function test 4")
from functools import wraps


def logit(logfile='out.log'):
    def logging_decorator(func):
        @wraps(func)
        def wrapped_function(*args, **kwargs):
            log_string = func.__name__+"was called"
            print(log_string)
            with open(logfile, 'a') as opened_file:
                opened_file.write(log_string+"\n")
            return func(*args, **kwargs)
        return wrapped_function
    return logging_decorator

@logit()
def myfunc1():
    pass

myfunc1()

@logit(logfile='func2.log')
def myfunc2():
    pass

myfunc2()

print("decoration test class ")
from functools import wraps


class logit(object):
    def __init__(self, logfile='out.log') -> None:
        self.logfile = logfile
    def __call__(self, func):
        @wraps(func)
        def wrapped_function(*args, **kwargs):
            log_string = func.__name__+" was called"
            print(log_string)
            with open(self.logfile,'a') as opened_file:
                opened_file.write(log_string+'\n')
            self.notify()
            return func(*args, **kwargs)
        return wrapped_function
    
    def notify(self):
        print("class 1")

@logit()
def myfunc1():
    pass

myfunc1()

class email_logit(logit):
    def __inti__(self, email='ad@df',*args, **kwargs):
        self.email = email
        super(logit, self).__init__(*args, **kwargs)   #当没有初始化类的时候，类中的方法可以当函数用，调用__call__方法。
    def notify(self):                                                               #当子类没有__call__方法时候，就是会寻找父类的实现。同样的，优先调用字类的同名函数
        print("class 2")

@email_logit()
def myfunc2():
    pass
myfunc2()
